package com.aaa.sso.config;

import com.aaa.entity.LoginInfo;
import com.aaa.sso.feign.LoginLogFeign;
import com.aaa.sso.feign.UserFeign;
import com.aaa.sso.service.MyUserDetailsService;
import com.aaa.utils.TokenUtils;
import com.aaa.utils.WebUtil;
import com.aaa.vo.Result;
import com.alibaba.fastjson.JSON;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;

import javax.annotation.Resource;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.time.LocalDateTime;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Configuration
public class MySecurityConfig extends WebSecurityConfigurerAdapter {

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Resource
    private MyUserDetailsService myUserDetailsService;

    @Resource
    private UserFeign userFeign;

    @Resource
    private LoginLogFeign loginLogFeign;

    //redis模板
    @Autowired
    private StringRedisTemplate redisTemplate;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //userDetailsService:传递一个UserDetailsService类对象。查询数据库完成相应的功能
        auth.userDetailsService(myUserDetailsService).passwordEncoder(passwordEncoder());
    }

//    @Resource
//    private LoginFilter loginFilter;

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        //将自定义过滤器添加到过滤链中
//        http.addFilterBefore(loginFilter, UsernamePasswordAuthenticationFilter.class);

        //登陆成功后放行其他路径
        http.formLogin()
                .loginProcessingUrl("/login") //指定自定义登陆处理路径
                .successHandler(successHandler()) //成功处理类
                .failureHandler(failureHandler()) //失败处理类
                .permitAll();
        //权限不足
        http.exceptionHandling().accessDeniedHandler(accessDeniedHandler());
        //禁用CSRF的校验
        http.csrf().disable();
        //设置跨域
        http.cors();
        //设置其他请求的验证
        http.authorizeRequests().anyRequest().authenticated();
    }

    //权限不足执行类
    public AccessDeniedHandler accessDeniedHandler() {
        return (request, response, accessDeniedException) -> {
            response.setContentType("application/json;charset=utf-8");//设置相应编码格式
            PrintWriter writer = response.getWriter();//开启写入流打印到前端
            Result<String> result = new Result(403, "权限不足", null);
            //把对象转化为json字符串
            String jsonString = JSON.toJSONString(result);
            //响应给前端
            writer.print(jsonString);
            //关闭
            writer.flush();
            writer.close();
        };
    }
    //认证失败后处理的功能
    public AuthenticationFailureHandler failureHandler(){
        return (request, response, exception) -> {
            response.setContentType("application/json;charset=utf-8");//设置相应编码格式
            PrintWriter writer = response.getWriter();//开启写入流打印到前端
            Result<String> result=new Result(500,exception.getMessage(),null);
            //把对象转化为json字符串
            String jsonString = JSON.toJSONString(result);
            //响应给前端
            writer.print(jsonString);
            //关闭
            writer.flush();
            writer.close();
        };
    }

    //认证成功后处理的功能
    public AuthenticationSuccessHandler successHandler(){
        return (request, response, authentication) -> {
            response.setContentType("application/json;charset=utf-8");//设置相应编码格式
            PrintWriter writer = response.getWriter();//开启写入流打印到前端
            //获取登陆成功的用户名
            User user = (User) authentication.getPrincipal();
            String username = user.getUsername();
            //获取当前用户的权限
            Collection<GrantedAuthority> authorities = user.getAuthorities();
            List<String> collect = authorities.stream().map(item -> item.getAuthority()).collect(Collectors.toList());
            //把用户名和权限封装到map对象
            Map<String,Object> map = new HashMap<>();
            map.put("username",username);
            map.put("authorities",collect);
            //生成token
            String token = TokenUtils.getToken(map);
            //如何刷新过期时间--使用redis
            //为了方便刷新token令牌的过期时间
            redisTemplate.opsForValue().set(token, JSON.toJSONString(map),30, TimeUnit.MINUTES);
            /**
             * 登录成功日志开始
             */
            com.aaa.entity.User user1 = userFeign.getByUsername(username);
            //获取当前用户的用户名
            String userName = user1.getUserName();
            //获取登录账户
            String login_account = user1.getPhone();
            //获取登录Ip登录地址
            String ip_addr = request.getRemoteAddr();
            //操作地点login_location
            String login_location = ip_addr.startsWith("192.168.") ? "内网IP" : "外网IP";
            //获取浏览器类型browser
            String agent = request.getHeader("user-agent");
            //判断字符串，Edge、Chrome、Safari、Firefox、IE浏览器或其它
            String browser="";
            if (agent.contains("Edge")) {
                browser="Edge";
            }
            else if (agent.contains("Chrome")) {
                browser="Chrome";
            }
            else if (agent.contains("Safari")) {
                browser="Safari";
            }
            else if (agent.contains("Firefox")) {
                browser="Firefox";
            }
            else {
                browser="IE或者其他";
            }
            //获取操作系统  写死不会
            String os="Windows 11";
            //登录状态
            String login_status="0";
            //登录类型
            String login_type="0";
            //提示信息
            String msg="登录成功";
            //登录时间login_time
            LocalDateTime login_time = LocalDateTime.now();
            LoginInfo loginInfo = new LoginInfo(null,userName,login_account,ip_addr,login_location,browser,os,login_status,login_type,msg,login_time);
            loginLogFeign.addLoginLog(loginInfo);
            /**
             * 登录成功日志结束
             */

            //响应一个公共实体类
            Result<String> result = new Result<>(200,"登录成功",token);
            //把对象转化为json字符串
            String jsonString = JSON.toJSONString(result);
            //响应给前端
            writer.print(jsonString);
            writer.flush();
            writer.close();
        };
    }
}
